﻿using System;
using System.IO.Ports;
using System.Threading;

namespace SerialPortTrans2
{
    /// <summary>
    /// 表示串口双向转发服务例程。
    /// </summary>
    public sealed class ServiceRoutine : IDisposable
    {
        private SerialPort m_port1;
        /// <summary>
        /// 串口1。
        /// </summary>
        public SerialPort Port1
        {
            get { return m_port1; }
        }

        private SerialPort m_port2;
        /// <summary>
        /// 串口2。
        /// </summary>
        public SerialPort Port2
        {
            get { return m_port2; }
        }

        private bool m_running;
        /// <summary>
        /// 是否运行。
        /// </summary>
        public bool Running
        {
            get { return m_running; }
        }

        /// <summary>
        /// PORT1 to PORT2
        /// </summary>
        private Thread m_thread1;
        /// <summary>
        /// PORT2 to PORT1
        /// </summary>
        private Thread m_thread2;

        /// <summary>
        /// 
        /// </summary>
        private AutoResetEvent m_event1 = new AutoResetEvent(false);
        /// <summary>
        /// 
        /// </summary>
        private AutoResetEvent m_event2 = new AutoResetEvent(false);

        /// <summary>
        /// 当接收到数据后引发此事件。
        /// </summary>
        public event Action<ServiceRoutine, SerialPort, byte[], int> DataReceived;
        /// <summary>
        /// 当发送异常产生后引发此事件。
        /// </summary>
        public event Action<ServiceRoutine, SerialPort, byte[], int, Exception> SendErrorOccurred;

        /// <summary>
        /// 初始化串口双向转发服务例程。
        /// </summary>
        /// <param name="port1">串口1</param>
        /// <param name="port2">串口2</param>
        public ServiceRoutine(SerialPort port1, SerialPort port2)
        {
            m_port1 = port1;
            m_port1.ReadTimeout = 1000;
            m_port1.WriteTimeout = 10000;
            m_port1.DataReceived += OnSerialPort1DataReceived;

            m_port2 = port2;
            m_port2.ReadTimeout = 1000;
            m_port2.WriteTimeout = 10000;
            m_port2.DataReceived += OnSerialPort2DataReceived;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSerialPort1DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            m_event1.Set();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSerialPort2DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            m_event2.Set();
        }

        /// <summary>
        /// 初始化串口双向转发服务例程。
        /// </summary>
        /// <param name="port1">串口1配置</param>
        /// <param name="port2">串口2配置</param>
        public ServiceRoutine(SerialPortConfigSection port1, SerialPortConfigSection port2)
            : this(port1.Create(), port2.Create())
        {
            //NOTHING
        }

        /// <summary>
        /// 执行处理。
        /// </summary>
        /// <param name="readPort"></param>
        /// <param name="writePort"></param>
        /// <returns></returns>
        private bool Exec(SerialPort readPort, SerialPort writePort)
        {
            int bytesToRead = readPort.BytesToRead;
            if (bytesToRead > 0)
            {
                byte[] byteArray = new byte[bytesToRead];
                int byteCount = readPort.Read(byteArray, 0, bytesToRead);
                if (byteCount > 0)
                {
                    var dataReceived = DataReceived;
                    if (dataReceived != null)
                    {
                        dataReceived.Invoke(this, readPort, byteArray, byteCount);
                    }
                    try
                    {
                        writePort.Write(byteArray, 0, byteCount);
                    }
                    catch (Exception ex)
                    {
                        var sendErrorOccurred = SendErrorOccurred;
                        if (sendErrorOccurred != null)
                        {
                            sendErrorOccurred.Invoke(this, writePort, byteArray, byteCount, ex);
                        }
                    }
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// THREAD1：PORT1 to PORT2
        /// </summary>
        private void Run1()
        {
            while (m_running)
            {
                m_event1.WaitOne();
                while (m_running)
                {
                    try
                    {
                        if (!Exec(m_port1, m_port2))
                        {
                            break;
                        }
                    }
                    catch
                    {
                        //NOTHING
                    }
                }
            }
        }
        /// <summary>
        /// THREAD2：PORT1 to PORT2
        /// </summary>
        private void Run2()
        {
            while (m_running)
            {
                m_event2.WaitOne();
                while (m_running)
                {
                    try
                    {
                        if (!Exec(m_port2, m_port1))
                        {
                            break;
                        }
                    }
                    catch
                    {
                        //NOTHING
                    }
                }
            }
        }

        /// <summary>
        /// 启动处理。
        /// </summary>
        public void Start()
        {
            lock (this)
            {
                if (!m_running)
                {
                    m_port1.Open();
                    m_port2.Open();

                    m_running = true;

                    m_thread1 = new Thread(Run1);
                    m_thread1.Name = string.Format("THREAD_{0}_{1}_TRANS", m_port1.PortName, m_port2.PortName);
                    m_thread1.IsBackground = true;
                    m_thread1.Start();

                    m_thread2 = new Thread(Run2);
                    m_thread2.Name = string.Format("THREAD_{0}_{1}_TRANS", m_port2.PortName, m_port1.PortName);
                    m_thread2.IsBackground = true;
                    m_thread2.Start();
                }
            }
        }

        /// <summary>
        /// 关闭处理。
        /// </summary>
        public void Close()
        {
            lock (this)
            {
                if (m_running)
                {
                    m_running = false;
                    m_event1.Set();
                    m_event2.Set();

                    m_thread1.Join();
                    m_thread2.Join();

                    m_port1.Close();
                    m_port2.Close();
                }
            }
        }

        /// <summary>
        /// 释放当前对象所占用的相关资源。
        /// </summary>
        public void Dispose()
        {
            lock (this)
            {
                if (m_running)
                {
                    m_running = false;
                    m_event1.Set();
                    m_event2.Set();

                    m_thread1.Join();
                    m_thread2.Join();
                }

                if (m_port1 != null)
                {
                    m_port1.Dispose();
                    m_port1 = null;
                }
                if (m_port2 != null)
                {
                    m_port2.Dispose();
                    m_port2 = null;
                }

                if (m_event1 != null)
                {
                    m_event1.Dispose();
                    m_event1 = null;
                }
                if (m_event2 != null)
                {
                    m_event2.Dispose();
                    m_event2 = null;
                }
            }
        }
    }
}